package references

import (
	"lesson01/instructions/base"
	"lesson01/rtda"
	"lesson01/rtda/heap"
)

type InvokeSpecial struct {
	base.Index16Instruction
}

func (e *InvokeSpecial) Execute(frame *rtda.Frame) {
	//先拿到当前类、当前常量池、方法符号引用，然后解析符号引用，拿到解析后的类和方法。
	currentClass := frame.Method().Class()
	cp := currentClass.ConstantPool()
	methodRef := cp.GetConstant(e.Index).(*heap.MethodRef)
	resolvedClass := methodRef.ResolvedClass()
	resolveMethod := methodRef.ResolveMethod()

	//假定从方法符号引用中解析出来的类是C，方法是M。如果M是构造函数，则声明M的类必须是C
	if resolveMethod.Name() == "<init>" && resolveMethod.Class() != resolvedClass {
		panic("java.lang.NoSuchMethodError")
	}

	if resolveMethod.IsStatic() {
		panic("java.lang.IncompatibleClassChangeError")
	}

	//从操作数栈中弹出this引用，如果该引用是null，抛出NullPointerException异常。
	ref := frame.OperandStack().GetRefFromTop(resolveMethod.ArgSlotCount() - 1)
	if ref == nil {
		panic("java.lang.NullPointerException")
	}

	if resolveMethod.IsProtected() &&
		resolveMethod.Class().IsSuperClassOf(currentClass) &&
		resolveMethod.Class().GetPackageName() != currentClass.GetPackageName() &&
		ref.Class() != currentClass &&
		!ref.Class().IsSubClassOf(currentClass) {

		panic("java.lang.IllegalAccessError")
	}

	methodToBeInvoked := resolveMethod
	if currentClass.IsSuper() &&
		resolvedClass.IsSuperClassOf(currentClass) &&
		resolveMethod.Name() != "<init>" {

		methodToBeInvoked = heap.LookupMethodInClass(currentClass.SuperClass(),
			methodRef.Name(), methodRef.Descriptor())
	}

	if methodToBeInvoked == nil || methodToBeInvoked.IsAbstract() {
		panic("java.lang.AbstractMethodError")
	}

	base.InvokeMethod(frame, methodToBeInvoked)
}
